import json
import logging

from django.contrib.auth.decorators import login_required
from django.contrib.auth.views import logout

from django.shortcuts import render, redirect
from django.views import View
from django import http
import re
from django.db import DatabaseError
from django.urls import reverse
from django.contrib.auth import login, authenticate
from django_redis import get_redis_connection

from carts.utils import merge_cart_cookie_to_redis
from goods.models import SKU
from users.models import User, Address
from meiduo_mall.utils.response_code import RETCODE

from meiduo_mall.utils.views import LoginRequiredJSONMixin
from . import constants

# 创建日志输出器
from users.utils import generate_verify_email_url,send_email

logger = logging.getLogger('django')




class UserBrowseHistory(LoginRequiredJSONMixin,View):
    """用户浏览商品记录"""
    def post(self,request):
        """保存用户浏览记录"""
        # 接受参数
        json_dict = json.loads(request.body.decode())
        sku_id = json_dict.get('sku_id')
        # 校验参数
        try:
            SKU.objects.get(id=sku_id)
        except SKU.DoesNotExist:
            return http.HttpResponseForbidden('sku不存在')
        # 保存用户浏览数据
        redis_conn = get_redis_connection('history')
        # 创建redis管道对象
        pl = redis_conn.pipeline()
        user_id = request.user.id

        # 先去重
        pl.lrem('history_%s' % user_id,0,sku_id)
        # 在存储
        pl.lpush('history_%s' % user_id,sku_id)
        # 最后截取
        pl.ltrim('history_%s' % user_id,0,4)
        # 执行管道
        pl.execute()

        return http.JsonResponse({'code': RETCODE.OK, 'errmsg': 'OK'})

    def get(self,request):
        """获取用户浏览记录"""
        # 获取Redis存储的sku_id列表信息
        redis_conn = get_redis_connection('history')
        sku_ids = redis_conn.lrange('history_%s' % request.user.id, 0, -1)

        # 根据sku_ids列表数据，查询出商品的sku信息
        skus=[]
        for sku_id in sku_ids:
            sku = SKU.objects.get(id=sku_id)
            skus.append({
                'id':sku.id,
                'name':sku.name,
                'default_image_url':sku.default_image.url,
                'price':sku.price

            })
        return http.JsonResponse({'code': RETCODE.OK, 'errmsg': 'OK', 'skus': skus})



class UpdateTitleAddressView(LoginRequiredJSONMixin,View):
    """修改地址标题"""

    def put(self,request,address_id):
        """修改地址标题"""
        # 接收参数
        json_dict = json.loads(request.body.decode())
        title = json_dict.get('title')

        try:
            # 查询地址
            adress = Address.objects.get(id=address_id)
            # 更新地址标题
            adress.title=title
            adress.save()
        except Exception as e:
            logger.error(e)
            return http.JsonResponse({'code': RETCODE.DBERR, 'errmsg': '设置地址标题失败'})
        # 响应修改地址标题结果
        return http.JsonResponse({'code': RETCODE.OK, 'errmsg': '设置地址标题成功'})
    pass


class DefaultAddressView(LoginRequiredJSONMixin,View):
    """设置默认地址"""

    def put(self, request, address_id):
        """设置默认地址"""
        try:
            # 接收参数，查询地址
            address = Address.objects.get(id=address_id)
            # 设置地址为默认地址
            request.user.default_address_id= address
            request.user.save()
        except Exception as e:
            logger.error(e)
            return http.JsonResponse({'code': RETCODE.DBERR, 'errmsg': '设置默认地址失败'})
        # 响应设置默认地址结果
        return http.JsonResponse({'code': RETCODE.OK, 'errmsg': '设置默认地址成功'})




class UpdateDestroyAddressView(LoginRequiredJSONMixin,View):
    """修改地址和删除地址"""
    def put(self,request,address_id):
        # 接受参数
        json_dict=json.loads(request.body.decode())
        receiver = json_dict.get('receiver')
        province_id = json_dict.get('province_id')
        city_id = json_dict.get('city_id')
        district_id = json_dict.get('district_id')
        place = json_dict.get('place')
        mobile = json_dict.get('mobile')
        tel = json_dict.get('tel')
        email = json_dict.get('email')

        # 校验参数
        if not all([receiver, province_id, city_id, district_id, place, mobile]):
            return http.HttpResponseForbidden('缺少必传参数')
        if not re.match(r'^1[3-9]\d{9}$', mobile):
            return http.HttpResponseForbidden('参数mobile有误')
        if tel:
            if not re.match(r'^(0[0-9]{2,3}-)?([2-9][0-9]{6,7})+(-[0-9]{1,4})?$', tel):
                return http.HttpResponseForbidden('参数tel有误')
        if email:
            if not re.match(r'^[a-z0-9][\w\.\-]*@[a-z0-9\-]+(\.[a-z]{2,5}){1,2}$', email):
                return http.HttpResponseForbidden('参数email有误')

        # 判断地址是否存在，并更新地址信息
        try:
            Address.objects.filter(id=address_id).update(
                user=request.user,
                title=receiver,
                receiver=receiver,
                province_id=province_id,
                city_id=city_id,
                district_id=district_id,
                place=place,
                mobile=mobile,
                tel=tel,
                email=email
            )
            pass
        except Exception as e:
            logger.error(e)
            return http.JsonResponse({'code': RETCODE.DBERR, 'errmsg': '更新地址失败'})
        # 地址更新成功，构造响应数据
        # 查询出更改后的地址信息，根据address_id
        address = Address.objects.get(id=address_id)
        # 把Address地址对象中的数据提取出来转换成address_dict数据字典
        address_dict={
            "id": address.id,
            "title": address.title,
            "receiver": address.receiver,
            "province": address.province.name,
            "city": address.city.name,
            "district": address.district.name,
            "place": address.place,
            "mobile": address.mobile,
            "tel": address.tel,
            "email": address.email
        }
        # 响应更新地址结果
        return http.JsonResponse({'code': RETCODE.OK, 'errmsg': '更新地址成功', 'address': address_dict})

    def delete(self,request,address_id):
        """删除地址"""
        try:
            # 查询要删除的地址
            address = Address.objects.get(id=address_id)
            # 将地址逻辑删除设置为True
            address.is_deleted=True
            address.save()
        except Exception as e:
            logger.error(e)
            return http.JsonResponse({'code': RETCODE.DBERR, 'errmsg': '删除地址失败'})
        # 响应删除地址结果
        return http.JsonResponse({'code': RETCODE.OK, 'errmsg': '删除地址成功'})



class CreateAddressView(LoginRequiredJSONMixin,View):
    """新增地址"""
    def post(self,request):
        """新增地址逻辑"""
        # 判断地址上限是否超过20
        count = request.user.addresses.count()
        if count >=constants.USER_ADDRESS_COUNTS_LIMIT:
            return http.JsonResponse({'code': RETCODE.THROTTLINGERR, 'errmsg': '超过地址数量上限'})

        # 接收参数
        json_dict = json.loads(request.body.decode())
        receiver = json_dict.get('receiver')
        province_id = json_dict.get('province_id')
        city_id = json_dict.get('city_id')
        district_id = json_dict.get('district_id')
        place = json_dict.get('place')
        mobile = json_dict.get('mobile')
        tel = json_dict.get('tel')
        email = json_dict.get('email')

        if not all([receiver, province_id, city_id, district_id, place, mobile]):
            return http.HttpResponseForbidden('缺少必传参数')
        if not re.match(r'^1[3-9]\d{9}$', mobile):
            return http.HttpResponseForbidden('参数mobile有误')
        if tel:
            if not re.match(r'^(0[0-9]{2,3}-)?([2-9][0-9]{6,7})+(-[0-9]{1,4})?$', tel):
                return http.HttpResponseForbidden('参数tel有误')
        if email:
            if not re.match(r'^[a-z0-9][\w\.\-]*@[a-z0-9\-]+(\.[a-z]{2,5}){1,2}$', email):
                return http.HttpResponseForbidden('参数email有误')
        # 保存地址信息
        try:
            address = Address.objects.create(
                user=request.user,
                title=receiver,
                receiver=receiver,
                province_id=province_id,
                city_id=city_id,
                district_id=district_id,
                place=place,
                mobile=mobile,
                tel=tel,
                email=email
            )
            # 设置默认地址
            if not request.user.default_address:
                request.user.default_address = address
                request.user.save()
        except Exception as e:
            logger.error(e)
            return http.JsonResponse({'code':RETCODE.DBERR,'errmsg': '新增地址失败'})
        # 新增地址成功
        address_list = {
            "id": address.id,
            "title": address.title,
            "receiver": address.receiver,
            "province": address.province.name,
            "city": address.city.name,
            "district": address.district.name,
            "place": address.place,
            "mobile": address.mobile,
            "tel": address.tel,
            "email": address.email
        }
        return http.JsonResponse({'code': RETCODE.OK, 'errmsg': '新增地址成功','address':address_list})

class AddressView(LoginRequiredJSONMixin,View):
    """用户收货地址"""

    def get(self,request):
        """提供收货地址界面"""
        # 获取用户地址列表
        login_user = request.user
        addresses = Address.objects.filter(user=login_user,is_deleted=False)
        address_dict_list=[]
        for address in addresses:
            address_dict={
                "id": address.id,
                "title": address.title,
                "receiver": address.receiver,
                "province": address.province.name,
                "city": address.city.name,
                "district": address.district.name,
                "place": address.place,
                "mobile": address.mobile,
                "tel": address.tel,
                "email": address.email
            }
            address_dict_list.append(address_dict)
        context = {
            #'default_address_id': login_user.default_address_id or '0'
            'default_address_id': login_user.default_address_id or 0,
            'addresses': address_dict_list,
        }

        return render(request,'user_center_site.html',context)


class EmailView(LoginRequiredJSONMixin,View):
    """定义邮箱绑定视图"""
    def put(self,request):
        """实现绑定邮箱逻辑"""
        # 接受参数 由于前端传入到后台数据是bytes类型，需要转换成json字典数据
        json_dict = json.loads(request.body.decode())
        email = json_dict.get('email')
        # 校验参数
        if not email:
            return http.HttpResponseForbidden('缺少email参数')
        if not re.match(r'^[a-z0-9][\w\.\-]*@[a-z0-9\-]+(\.[a-z]{2,5}){1,2}$', email):
            return http.HttpResponseForbidden('参数email有误')

        # 赋值email字段,将email数据保存到数据库
        try:
            request.user.email = email
            request.user.save()
        except Exception as e:
            logger.error(e)
            return http.JsonResponse({'code':RETCODE.DBERR,'errmsg':'添加邮箱失败'})
        # 异步发送验证邮件
        verify_url = generate_verify_email_url(request.user)

        #send_verify_email.delay(email, verify_url)
        send_email(email, verify_url)
        # 响应添加邮箱结果
        return http.JsonResponse({'code':RETCODE.OK,'errmsg':'添加邮箱成功'})


# 展示用户中心界面
class LoginRequiedMixin(object):
    """验证用户是否登录扩展类"""

    @classmethod
    def as_view(cls, **initkwargs):
        # 自定义的as_view()方法中，调用父类的as_view()方法
        view = super().as_view()
        return login_required(view)


class UserInfoView(LoginRequiedMixin, View):
    """用户中心"""
    def get(self, request):
        context = {
            'username':request.user.username,
            'mobile':request.user.mobile,
            'email':request.user.email,
            'email_active':request.user.email_active
        }

        return render(request, 'user_center_info.html',context)



# 退出登录
class LogoutView(View):
    """退出登录"""

    def get(self, request):
        # 清理session
        logout(request)
        # 退出登录，重定向到登录页
        response = redirect(reverse('contents:index'))
        # 退出登录时清除cookie中的username
        response.delete_cookie('username')

        return response


# 用户登录
class LoginView(View):

    def get(self, request):

        return render(request, 'login.html')

    def post(self, request):
        """
        实现登录逻辑
        :param request: 请求对象
        :return: 登录结果
        """
        # 接受参数
        username = request.POST.get('username')
        password = request.POST.get('password')
        remembered = request.POST.get('remembered')
        # 校验参数
        # 判断参数是否齐全
        if not all([username, password]):
            return http.HttpResponseForbidden("缺少必传参数")
        # 判断用户名是否是5-20个字符
        if not re.match(r'^[a-zA-Z0-9_-]{5,20}$', username):
            return http.HttpResponseForbidden('请输入正确的用户名或手机号')
        # 判断密码是否是8-20个字符
        if not re.match(r'^[0-9A-Za-z]{8,20}$', password):
            return http.HttpResponseForbidden('密码最少8位，最长20位')

        # 认证登录用户
        user = authenticate(username=username, password=password)
        if user is None:
            return render(request, 'login.html', {'account_errmsg': '用户名或密码错误'})
        # 实现状态保持
        login(request, user)
        if remembered != 'on':
            # 没有记住用户：浏览时关闭会话结束
            request.session.set_expiry(0)
        else:
            # 记住用户名：None表示两周后过期
            request.session.set_expiry(None)

        # 响应登录结果
        response = redirect(reverse('contents:index'))

        # 登录时用户名写入到cookie，有效期15天
        response.set_cookie('username', user.username, max_age=3600 * 24 * 15)
        # 合并购物车
        response = merge_cart_cookie_to_redis(request=request, user=user, response=response)
        # 响应登录结果
        return response


# 用户名重复验证
class UsernameCountView(View):

    def get(self, request, username):
        """

        :param request: 请求对象
        :param username: 用户名
        :return: 响应前端数据
        """
        # 根据用户名从数据库查询数据个数
        count = User.objects.filter(username=username).count()
        return http.JsonResponse({'code': RETCODE.OK, 'errmsg': 'OK', 'count': count})


# 验证手机号是否重复
class MobileCountView(View):

    def get(self, request, mobile):
        """
        :param request: 请求对象
        :param mobile: 请求手机号
        :return: 响应前端的结果
        """
        count = User.objects.filter(mobile=mobile).count()
        return http.JsonResponse({'code': RETCODE.OK, 'errmsg': 'OK', 'count': count, 'content-Type': 'application/json'})


# 用户注册
class RegisterView(View):

    def get(self, request):

        return render(request, 'register.html')

    def post(self, request):
        # 获取表单数据
        username = request.POST.get('username')
        password = request.POST.get('password')
        password2 = request.POST.get('password2')
        mobile = request.POST.get('mobile')
        allow = request.POST.get('allow')
        # 校验表单数据
        # 判断表单参数是否齐全
        if not all([username, password, password2, mobile, allow]):
            return http.HttpResponseForbidden('缺少必要的参数')
        if not re.match(r'^[0-9a-zA-Z_-]{5,20}$', username):
            return http.HttpResponseForbidden('请输入5-20个字符的用户名')
        # 判断密码是否是8-20个数字
        if not re.match(r'^[0-9A-Za-z]{8,20}$', password):
            return http.HttpResponseForbidden('请输入8-20位的密码')
        # 判断两次密码是否一致
        if password != password2:
            return http.HttpResponseForbidden('两次输入的密码不一致')
        # 判断手机号是否合法
        if not re.match(r'^1[3-9]\d{9}$', mobile):
            return http.HttpResponseForbidden('请输入正确的手机号码')
        # 判断是否勾选用户协议
        if allow != 'on':
            return http.HttpResponseForbidden('请勾选用户协议')
        # 存储表单数据
        try:
            user = User.objects.create_user(username=username, password=password, mobile=mobile)
        except DatabaseError:
            return render(request, 'register.html', {'register_errmsg': '注册失败'})
        # 响应注册结果
        response = redirect(reverse('contents:index'))
        # 注册时用户名写入到cookie，有效期15天
        response.set_cookie('username', user.username, max_age=3600 * 24 * 15)
        # 登入用户，实现状态保持
        login(request, user)
        # 响应客户端
        return response


